package com.fansl.allround.upms.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fansl.allround.common.core.bean.SocialInfo;
import com.fansl.allround.common.core.bean.UserInfo;
import com.fansl.allround.common.core.constant.CacheConstants;
import com.fansl.allround.common.core.constant.CommonConstants;
import com.fansl.allround.common.core.constant.enums.OauthTypeEnum;
import com.fansl.allround.common.core.constant.enums.UserOauthStatusEnum;
import com.fansl.allround.common.core.util.R;
import com.fansl.allround.common.security.util.SecurityUtils;
import com.fansl.allround.upms.api.dto.BindSocialInfoDTO;
import com.fansl.allround.upms.api.dto.SysSocialUserDTO;
import com.fansl.allround.upms.api.dto.UnbindSocialInfoDTO;
import com.fansl.allround.upms.api.dto.UserDTO;
import com.fansl.allround.upms.api.entity.*;
import com.fansl.allround.upms.api.vo.UserVO;
import com.fansl.allround.upms.mapper.SysUserMapper;
import com.fansl.allround.upms.mapper.SysUserOauthMapper;
import com.fansl.allround.upms.service.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author fansl
 * @Description: 用户表 服务实现类
 * @date 2019/7/12 18:54
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
    private final SysMenuService sysMenuService;
    private final SysRoleService sysRoleService;
    private final SysDeptService sysDeptService;
    private final SysUserRoleService sysUserRoleService;
    private final SysDeptRelationService sysDeptRelationService;
    private final SysUserOauthMapper sysUserOauthMapper;

    /**
     * 通过ID查询用户信息
     *
     * @param id 用户ID
     * @return 用户信息
     */
    @Override
    public UserVO getUserVoById(Integer id) {
        return baseMapper.getUserVoById(id);
    }

    /**
     * 查询上级部门的用户信息
     *
     * @param username 用户名
     * @return R
     */
    @Override
    public List<SysUser> listAncestorUsersByUsername(String username) {
        SysUser sysUser = this.getOne(Wrappers.<SysUser>query().lambda()
                .eq(SysUser::getUsername, username));

        SysDept sysDept = sysDeptService.getById(sysUser.getDeptId());
        if (sysDept == null) {
            return null;
        }

        Integer parentId = sysDept.getParentId();
        return this.list(Wrappers.<SysUser>query().lambda()
                .eq(SysUser::getDeptId, parentId));
    }

    /**
     * 保存用户信息
     *
     * @param userDto DTO 对象
     * @return success/fail
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveUser(UserDTO userDto) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userDto, sysUser);
        sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
        sysUser.setPassword(ENCODER.encode(userDto.getPassword()));
        baseMapper.insert(sysUser);
        List<SysUserRole> userRoleList = userDto.getRole()
                .stream().map(roleId -> {
                    SysUserRole userRole = new SysUserRole();
                    userRole.setUserId(sysUser.getUserId());
                    userRole.setRoleId(roleId);
                    return userRole;
                }).collect(Collectors.toList());
        return sysUserRoleService.saveBatch(userRoleList);
    }

    /**
     * 重置初始密码
     *
     * @param userId
     * @return 重置后的密码
     */
    @Override
    public String resetInitPassword(Integer userId) {
        String pwd = RandomUtil.randomString(6);
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setInitPassword(pwd);
        this.updateById(sysUser);
        return pwd;
    }

    /**
     * 更新用户角色
     *
     * @param userId
     * @param roleIdList
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUserRole(Integer userId, List<Integer> roleIdList) {
        List<SysUserRole> sysUserRoles = new ArrayList<>();
        roleIdList.forEach(roleId -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(userId);
            sysUserRole.setRoleId(roleId);
            sysUserRoles.add(sysUserRole);
        });
        sysUserRoleService.removeRoleByUserId(userId);
        sysUserRoleService.saveBatch(sysUserRoles);
        return Boolean.TRUE;
    }

    /**
     * 注册新用户
     *
     * @param socialUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserInfo createSocialUser(SysSocialUserDTO socialUser) {
        SysUser user = socialUser.getSysUser();
        //创建新用户
        this.save(user);
        //绑定社交信息
        BindSocialInfoDTO bindInfo = new BindSocialInfoDTO();
        bindInfo.setUserId(user.getUserId());
        bindInfo.setOpenId(socialUser.getOpenId());
        bindInfo.setOauthType(socialUser.getOauthType());
        bindSocialInfo(bindInfo);
        //给用户添加默认角色
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setRoleId(1);
        sysUserRole.setUserId(user.getUserId());
        sysUserRoleService.ignoreSave(sysUserRole);
        return getUserInfo(user);
    }

    /**
     * 绑定用户社交信息
     *
     * @param bindInfo
     * @return
     */
    @Override
    public Boolean bindSocialInfo(BindSocialInfoDTO bindInfo) {
        int count = sysUserOauthMapper.selectCount(Wrappers.<SysUserOauth>lambdaQuery()
                .eq(SysUserOauth::getOauthId, bindInfo.getOauthType().getCode())
                .eq(SysUserOauth::getOpenId, bindInfo.getOpenId().getBytes()));
        if (count == 0) {
            SysUserOauth sysUserOauth = new SysUserOauth();
            sysUserOauth.setUserId(bindInfo.getUserId())
                    .setOauthId(bindInfo.getOauthType().getCode())
                    .setOpenId(bindInfo.getOpenId().getBytes())
                    .setStatus(UserOauthStatusEnum.BIND.getCode());
            return sysUserOauthMapper.insert(sysUserOauth) > 0;
        } else {
            SysUserOauth sysUserOauth = new SysUserOauth();
            sysUserOauth.setUserId(bindInfo.getUserId())
                    .setOauthId(bindInfo.getOauthType().getCode())
                    .setOpenId(bindInfo.getOpenId().getBytes())
                    .setStatus(UserOauthStatusEnum.BIND.getCode());
            return sysUserOauthMapper.update(sysUserOauth, Wrappers.<SysUserOauth>lambdaQuery()
                    .eq(SysUserOauth::getOauthId, bindInfo.getOauthType().getCode())
                    .eq(SysUserOauth::getOpenId, bindInfo.getOpenId().getBytes())) > 0;
        }

    }

    /**
     * 根据openId获取用户信息
     *
     * @param oauthType
     * @param openid
     * @return
     */
    @Override
    public SysUser getByOauthType(OauthTypeEnum oauthType, String openid) {
        SysUserOauth sysUserOauth = sysUserOauthMapper.selectOne(Wrappers.<SysUserOauth>lambdaQuery()
                .eq(SysUserOauth::getOauthId, oauthType.getCode())
                .eq(SysUserOauth::getOpenId, openid));
        return Optional.ofNullable(sysUserOauth).map((s) -> {
            log.info("存在社交信息记录：{},{}，查找当前绑定的用户：{}", oauthType, openid, s.getUserId());
            return baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                    .eq(SysUser::getUserId, s.getUserId()));
        }).orElse(null);
    }

    /**
     * 解绑第三方应用
     *
     * @param unbindInfo 三方应用
     * @return
     */
    @Override
    public Boolean unbindSocialInfo(UnbindSocialInfoDTO unbindInfo) {
        SysUserOauth sysUserOauth = new SysUserOauth();
        sysUserOauth.setStatus(UserOauthStatusEnum.UNBIND.getCode());
        int count = sysUserOauthMapper.update(sysUserOauth, Wrappers.<SysUserOauth>lambdaQuery()
                .eq(SysUserOauth::getUserId, unbindInfo.getUserId())
                .eq(SysUserOauth::getOauthId, unbindInfo.getOauthType().getCode()));
        return count > 0;
    }

    /**
     * 查询用户信息
     *
     * @param sysUser 用户
     * @return userInfo
     */
    @Override
    public UserInfo getUserInfo(SysUser sysUser) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(sysUser.getUserId());
        userInfo.setUser(sysUser);
        //设置角色列表
        List<Integer> roleIds = sysRoleService.listRolesByUserId(sysUser.getUserId())
                .stream()
                .map(SysRole::getRoleId)
                .collect(Collectors.toList());
        userInfo.setRoles(ArrayUtil.toArray(roleIds, Integer.class));
        //设置权限列表
        List<String> userPermissions =
                sysMenuService.listPermissionByUserId(sysUser.getUserId());
        Set<String> permissions = userPermissions.stream()
                .filter(permission -> StrUtil.isNotEmpty(permission))
                .collect(Collectors.toSet());

        userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));
        //查询社交信息
        List<SysUserOauth> sysUserOauths = sysUserOauthMapper.selectList(Wrappers.<SysUserOauth>lambdaQuery()
                .eq(SysUserOauth::getUserId, sysUser.getUserId()));

        List<SocialInfo> socialInfoList = sysUserOauths.stream()
                .filter(sysUserOauth -> sysUserOauth.getStatus().equals(UserOauthStatusEnum.BIND.getCode()))
                .map(sysUserOauth -> {
                    SocialInfo socialInfo = new SocialInfo();
                    socialInfo.setOauthId(sysUserOauth.getOauthId());
                    socialInfo.setOauthType(OauthTypeEnum.getByCode(sysUserOauth.getOauthId()));
                    socialInfo.setOauthTypeDesc(OauthTypeEnum.getByCode(sysUserOauth.getOauthId()).getMessage());
                    socialInfo.setUserOauthId(sysUserOauth.getUserOauthId());
                    socialInfo.setOauthStatus(UserOauthStatusEnum.getByCode(sysUserOauth.getStatus()));
                    socialInfo.setOauthStatusDesc(UserOauthStatusEnum.getByCode(sysUserOauth.getStatus()).getDescription());
                    return socialInfo;
                }).collect(Collectors.toList());
        userInfo.setSocialInfoList(socialInfoList);
        return userInfo;
    }

    /**
     * 分页查询用户信息（含有角色信息）
     *
     * @param page    分页对象
     * @param userDTO 参数列表
     * @return
     */
    @Override
    public IPage getUserWithRolePage(Page page, UserDTO userDTO) {
        return baseMapper.getUserVosPage(page, userDTO);
    }

    /**
     * 删除用户
     *
     * @param sysUser 用户
     * @return boolean
     */
    @Override
    public Boolean removeUserById(SysUser sysUser) {
        sysUserRoleService.removeRoleByUserId(sysUser.getUserId());
        this.removeById(sysUser.getUserId());
        return Boolean.TRUE;
    }

    /**
     * 更新当前用户基本信息
     *
     * @param userDto 用户信息
     * @return Boolean
     */
    @Override
    @CacheEvict(value = CacheConstants.CACHE_USER_DETAIL, key = "#userDto.username")
    public R<Boolean> updateUserInfo(UserDTO userDto) {
        UserVO userVO = baseMapper.getUserVoByUsername(userDto.getUsername());
        SysUser sysUser = new SysUser();
        if (StrUtil.isNotBlank(userDto.getPassword())
                && StrUtil.isNotBlank(userDto.getNewpassword1())) {
            if (ENCODER.matches(userDto.getPassword(), userVO.getPassword())) {
                sysUser.setPassword(ENCODER.encode(userDto.getNewpassword1()));
            } else {
                log.warn("原密码错误，修改密码失败:{}", userDto.getUsername());
                return new R<>(Boolean.FALSE, "原密码错误，修改失败");
            }
        }
        sysUser.setPhone(userDto.getPhone());
        sysUser.setUserId(userVO.getUserId());
        sysUser.setAvatar(userDto.getAvatar());
        return new R<>(this.updateById(sysUser));
    }

    /**
     * 更新指定用户信息
     *
     * @param userDto 用户信息
     * @return
     */
    @Override
    @CacheEvict(value = CacheConstants.CACHE_USER_DETAIL, key = "#userDto.username")
    public Boolean updateUser(UserDTO userDto) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userDto, sysUser);
        sysUser.setUpdateTime(LocalDateTime.now());

        if (StrUtil.isNotBlank(userDto.getPassword())) {
            sysUser.setPassword(ENCODER.encode(userDto.getPassword()));
        }
        this.updateById(sysUser);

        sysUserRoleService.remove(Wrappers.<SysUserRole>update().lambda()
                .eq(SysUserRole::getUserId, userDto.getUserId()));
        userDto.getRole().forEach(roleId -> {
            SysUserRole userRole = new SysUserRole();
            userRole.setUserId(sysUser.getUserId());
            userRole.setRoleId(roleId);
            userRole.insert();
        });
        return Boolean.TRUE;
    }

    /**
     * 获取当前用户的子部门信息
     *
     * @return 子部门列表
     */
    private List<Integer> getChildDepts() {
        Integer deptId = SecurityUtils.getUser().getDeptId();
        //获取当前部门的子部门
        return sysDeptRelationService
                .list(Wrappers.<SysDeptRelation>query().lambda()
                        .eq(SysDeptRelation::getAncestor, deptId))
                .stream()
                .map(SysDeptRelation::getDescendant)
                .collect(Collectors.toList());
    }
}
